﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;

namespace NWebSockets
{
	public class WebSocketServer
	{
		/// <summary>
		/// Constructs a web socket server that listens on the specified port
		/// </summary>
		public WebSocketServer( int port )
		{
			this.Port = port;
			this.Clients = new List<WebSocketClient>();

			_listener = new TcpListener(IPAddress.Any, port);
		}

		#region Public Methods
		/// <summary>
		/// Starts the server
		/// </summary>
		public void Start()
		{
			// start listening for new connections
			_listener.Start();
			_listener.BeginAcceptTcpClient( OnAcceptTcpClient, null );
		}

		/// <summary>
		/// Stops the server
		/// </summary>
		public void Stop()
		{
			// shut the server down
			_listener.Stop();
		}
		#endregion

		#region Public Properties
		/// <summary>
		/// The port the server should listen on
		/// </summary>
		public int Port { get; private set; }

		/// <summary>
		/// A list of connected clients
		/// </summary>
		public List<WebSocketClient> Clients { get; private set; }
		#endregion

		#region Protected Methods
		/// <summary>
		/// An event that is raised when a client sends data to the server
		/// </summary>
		protected virtual void OnClientMessageReceived( object sender, WebSocketEventArgs e )
		{
			var client = sender as WebSocketClient;
			var message = e.Message;

			System.Diagnostics.Debug.Print( string.Format( "A client sent the following message: {0}", message ) );
		}

		/// <summary>
		/// An event that is raised when a client disconnects from the server
		/// </summary>
		protected virtual void OnClientDisconnected( object sender, EventArgs e )
		{
			var client = sender as WebSocketClient;

			System.Diagnostics.Debug.Print( "A client disconnected from the server" );
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// An event that is raised when a client connects to the server
		/// </summary>
		private void OnAcceptTcpClient( IAsyncResult result )
		{
			// get the connecting client
			var tcpClient = _listener.EndAcceptTcpClient( result );

			// receive their handshake
			var clientHandshake = new byte[1024];
			var clientHandshakeSize = tcpClient.Client.Receive( clientHandshake );

			// generate the server handshake
			var serverHandshake = WebSocketServerHandshake.FromClientHandshake( clientHandshake, clientHandshakeSize );

			// send the reply
			tcpClient.Client.Send( serverHandshake.ToBytes() );

			// subscribe to the message received event
			var websocketClient = new WebSocketClient( tcpClient );
			websocketClient.OnConnectionClosed += OnClientDisconnected;
			websocketClient.OnMessageReceived += OnClientMessageReceived;
			
			// add the client to the list of connected clients
			this.Clients.Add( websocketClient );
		}
		#endregion

		#region Private Members
		/// <summary>
		/// The internal socket listener
		/// </summary>
		private TcpListener _listener;
		#endregion
	}
}
